package xj.graph2d.shapes;

import java.awt.Graphics2D;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.HashMap;
import java.util.Map;

import xj.graph2d.DrawAttr;
import xj.graph2d.DrawShape;
import xj.graph2d.DrawShapeBase;
import xj.graph2d.GraphNodeBase;
import xj.graph2d.SelfLoop;

public class CubicCurveSelfLoop 
  extends CubicCurveShape 
  implements SelfLoop, ShapeConstants {

  public CubicCurveSelfLoop(float x1, float y1, float x2, float y2,
			    float height, int direction) {
    this(x1, y1, x2, y2, height, direction, false);
  }

  public CubicCurveSelfLoop(float x1, float y1, float x2, float y2,
			    float height, int direction, 
			    boolean createComp) {
    super(x1, y1, x2, y2, createComp);
    allowSelfLoop = true;
    setCurveType(C_SHAPE_CURVE);
    this.height = height;
    this.direction = direction;
    float cx1 = x1;
    float cy1 = y1;
    float cx2 = x2;
    float cy2 = y2;
    if (direction == NORTH) {
      cy1 = cy2 = Math.min(y1, y2) - height;
    } else if (direction == SOUTH) {
      cy1 = cy2 = Math.max(y1, y2) + height;
    } else if (direction == EAST) {
      cx1 = cx2 = Math.max(x1, x2) + height;
    } else if (direction == WEST) {
      cx1 = cx2 = Math.min(x1, x2) - height;
    }
    ctr1.setLocation(cx1, cy1);
    ctr2.setLocation(cx2, cy2);
    curve = null;
    calculateBounds();
  }

  public CubicCurveSelfLoop(boolean createComp) {
    this(20, NORTH, createComp);
  }

  public CubicCurveSelfLoop(float height, int direction, boolean createComp) {
    super(0, 0, 0, 0, createComp);
    allowSelfLoop = true;
    setCurveType(C_SHAPE_CURVE);
    this.height = height;
    this.direction = direction;
  }

  @Override
public DrawShape cloneDrawShape(boolean deepClone) 
    throws CloneNotSupportedException {
    CubicCurveSelfLoop loop = new CubicCurveSelfLoop((float) p1.getX(), (float) p1.getY(), 
						     (float) p2.getX(), (float) p2.getY(), 
						     height, direction, comp != null);
    copyTo(loop);
    cloneSubshapes(loop, deepClone);
    cloneModel(loop);
    return loop;
  }

  @Override
  public DrawShape makeInstance() {
    return new CubicCurveSelfLoop(height, direction, comp != null);
  }

  @Override
  public void copyTo(DrawShapeBase s) 
    throws CloneNotSupportedException {
    if (s != null) {
      super.copyTo(s);
      if (s instanceof CubicCurveSelfLoop) {
	CubicCurveSelfLoop r = (CubicCurveSelfLoop) s;
	r.height = height;
	r.direction = direction;
      }
    }
  }

  public boolean isOrthogonal() {
    return false; 
  }

  public boolean isOneSided() {
    return true; 
  }

  public int getDirection() {
    return direction;
  }

  public void setDirection(int direction) {
    this.direction = direction;
  }

  public float getHeight() {
    return height;
  }

  public void setHeight(float height) {
    this.height = height;
  }

  public float getWidth() {
    return 0;
  }

  public void setWidth(float width) {
  }

  public boolean isAdjustable() {
    return adjustable;
  }

  public void setAdjustable(boolean b) {
    adjustable = b;
  }

  @Override
  public void drawOutline(Graphics2D g2, float x1, float y1, float x2, float y2) {
    // adjustDirection(x1, y1, x2, y2);

    float cx1 = x1;
    float cy1 = y1;
    float cx2 = x2;
    float cy2 = y2;
    if (direction == NORTH) {
      cy1 = cy2 = Math.min(y1, y2) - height;
    } else if (direction == SOUTH) {
      cy1 = cy2 = Math.max(y1, y2) + height;
    } else if (direction == EAST) {
      cx1 = cx2 = Math.max(x1, x2) + height;
    } else if (direction == WEST) {
      cx1 = cx2 = Math.min(x1, x2) - height;
    }
    DrawAttr attr = getDrawAttr();
    if (attr != null) {
      g2.setColor(attr.getPenColor());
    } else {
      g2.setColor(DrawAttr.getDefaultPenColor());
    }
    g2.draw(new CubicCurve2D.Float(x1, y1, cx1, cy1, cx2, cy2, x2, y2));
  }

  /*
         * protected void adjustDirection(float x1, float y1, float x2, float
         * y2) { GraphNode src = getSource(); GraphNode des = getDestination();
         * if (src != null && src instanceof GraphNodeBase && src == des) {
         * GraphNodeBase node = (GraphNodeBase) src; Rectangle2D bounds =
         * node.getBounds(); adjustDirection(bounds, x1, y1, x2, y2); } }
         */

  public void adjustDirection(Rectangle2D bounds, float x1, float y1, float x2,
      float y2) {
    if (debug) {
      System.out.println("CubicCurveSelfLoop.adjustDirection()");
    }
    if (bounds != null) {
      float nx1 = (float) bounds.getX();
      float ny1 = (float) bounds.getY();
      float nx2 = nx1 + (float) bounds.getWidth();
      float ny2 = ny1 + (float) bounds.getHeight();

      float dx = Math.abs(x1 - x2);
      float dy = Math.abs(y1 - y2);
      float cx = (x2 + x1) / 2;
      float cy = (y2 + y1) / 2;
      if (dx >= dy) {
	// north or south
	if (Math.abs(cy - ny1) < Math.abs(cy - ny2)) {
	  direction = NORTH;
	} else {
	  direction = SOUTH;
	}
      } else {
	// east or west
	if (Math.abs(cx - nx1) < Math.abs(cx - nx2)) {
	  direction = WEST;
	} else {
	  direction = EAST;
	}
      }
    }
  }

  @Override
  public void adjustSelfLoop() {
    if (debug) {
      System.out.println("CubicCurveSelfLoop.adjustSelfLoop()");
    }
    float x1, y1, x2, y2;
    Point2D p1 = getEnd1();
    Point2D p2 = getEnd2();
    if (p1 != null && p2 != null) {
      x1 = (float) p1.getX();
      y1 = (float) p1.getY();
      x2 = (float) p2.getX();
      y2 = (float) p2.getY();

      float cx1 = x1;
      float cy1 = y1;
      float cx2 = x2;
      float cy2 = y2;
      if (direction == NORTH) {
	cy1 = cy2 = Math.min(y1, y2) - height;
      } else if (direction == SOUTH) {
	cy1 = cy2 = Math.max(y1, y2) + height;
      } else if (direction == EAST) {
	cx1 = cx2 = Math.max(x1, x2) + height;
      } else if (direction == WEST) {
	cx1 = cx2 = Math.min(x1, x2) - height;
      }
      ctr1.setLocation(cx1, cy1);
      ctr2.setLocation(cx2, cy2);
      curve = null;
      calculateBounds();

      /*
       * System.out.println("CubicCurveSelfLoop.adjustSelfLoop() " + " x1=" +
       * x1 + " y1=" + y1 + " x2=" + x2 + " y2=" + y2 + " x3=" + x3 + " y3=" +
       * y3);
       */
    }
  }

  @Override
  protected Map<String, Object> getXMLAttributes() {
    Map<String, Object> attrs = super.getXMLAttributes();
    if (attrs == null) {
      attrs = new HashMap<String, Object>();
    }
    attrs.put("height", height);
    attrs.put("direction", direction);
    return attrs;
  }

  @Override
  protected void processXMLAttributes(org.w3c.dom.Element node) {
    if (node != null) {
      super.processXMLAttributes(node);

      String s;
      s = node.getAttribute("height");
      try {
	height = Float.parseFloat(s);
      } catch (NumberFormatException ex) {
      }
      s = node.getAttribute("direction");
      try {
	direction = Integer.parseInt(s);
      } catch (NumberFormatException ex) {
      }
    }
  }

  @Override
  public void adjustControlPoints() {
  }

  @Override
  public void adjustEnds(GraphNodeBase src, GraphNodeBase des, 
			 boolean adjustCtrPts) {
    // System.out.println("CubicCurveSelfLoop.adjustEnds(boolean)");

    super.adjustEnds(src, des, adjustCtrPts);
    if (!adjustable) { 
      adjustSelfLoop();
    }
  }

  protected float height;
  protected int direction;
  protected boolean adjustable = true;

  protected static final boolean debug = false;

}
